package com.suxin.plugin.intergration.manager.plugin;
import com.suxin.plugin.intergration.manager.PluginWrapper;
import com.suxin.plugin.intergration.manager.finder.PluginDescriptor;
import com.suxin.plugin.intergration.manager.finder.PluginDescriptorFinder;
import com.suxin.plugin.intergration.manager.loader.PluginLoader;
import com.suxin.plugin.intergration.manager.repository.PluginRepository;
import com.suxin.plugin.pf4j.PluginFactory;
import com.suxin.plugin.pf4j.PluginState;
import com.suxin.plugin.pf4j.state.PluginStateEvent;
import com.suxin.plugin.pf4j.state.PluginStateListener;
import lombok.extern.slf4j.Slf4j;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Tang
 * @classname AbstractPluginManager
 * @description [  ]
 * @date 2022/6/26 23:21
 */
@Slf4j
public abstract class AbstractPluginManager implements PluginManager{

    protected Path pluginRoot;

    protected PluginRepository pluginRepository;
    protected PluginLoader pluginClassLoader;
    protected PluginDescriptorFinder pluginDescriptorFinder;
    protected com.suxin.plugin.pf4j.PluginFactory pluginFactory;

    /**
     * 插件实例存储map
     */
    protected Map<String,PluginWrapper> pluginWrapperMap;

    protected List<PluginWrapper> resolvePlugins;
    protected List<PluginWrapper> startedPlugins;

    protected List<PluginStateListener> pluginStateListeners;

    public AbstractPluginManager(Path pluginRoot) {
        this.pluginRoot = pluginRoot;
        initialize();
    }

    protected void initialize(){
        resolvePlugins = new ArrayList<>(10);
        startedPlugins = new ArrayList<>(10);
        pluginWrapperMap = new HashMap<>(16);
        // 插件位置存储
        pluginRepository = createPluginRepository();
        // 插件加载器
        pluginClassLoader = createPluginClassLoader();
        // 插件描述符读取器
        pluginDescriptorFinder = createPluginDescriptorFinder();
        // 插件工厂
        pluginFactory = createPluginFactory();
    }

    /**
     * PluginFactory
     * @return
     */
    public abstract com.suxin.plugin.pf4j.PluginFactory createPluginFactory();

    /**
     * PluginDescriptorFinder
     * @return
     */
    protected abstract PluginDescriptorFinder createPluginDescriptorFinder();

    /**
     * PluginLoader
     * @return
     */
    protected abstract PluginLoader createPluginClassLoader();

    /**
     * PluginRepository
     * @return
     */
    protected abstract PluginRepository createPluginRepository();

    @Override
    public List<PluginWrapper> getPlugins() {
        return new ArrayList<>(pluginWrapperMap.values());
    }

    @Override
    public PluginWrapper getPlugins(String pluginId) {
        return pluginWrapperMap.get(pluginId);
    }

    @Override
    public void loadPlugins() {
        log.info("开始加载插件目录：{}",pluginRoot);
        if (Files.notExists(pluginRoot) || !Files.isDirectory(pluginRoot)) {
            log.info("请检查插件目录！");
            return;
        }
        // 获取插件列表
        List<Path> pluginPaths = this.pluginRepository.getPluginPaths();
        for (Path pluginPath : pluginPaths) {
            loadPluginFromPath(pluginPath);
        }
    }

    private void loadPluginFromPath(Path pluginPath) {
        PluginDescriptorFinder descriptorFinder = getPluginDescriptorFinder();
        // 读取插件描述信息
        PluginDescriptor pluginDescriptor = descriptorFinder.find(pluginPath);
        validatePluginDescriptor(pluginDescriptor);
        String pluginId = pluginDescriptor.getPluginId();
        String pluginClassName = pluginDescriptor.getPluginClass();
        log.info("Class '{}' for plugin '{}'",pluginClassName,pluginId);
        log.info("Loading plugin '{}'", pluginPath);
        ClassLoader classLoader = getPluginClassLoader().loadPlugin(pluginPath);
        log.debug("Loaded plugin '{}' with class loader '{}'", pluginPath, classLoader);
        // 创建一个插件实例
        PluginWrapper pluginWrapper = new PluginWrapper(pluginPath, pluginDescriptor, classLoader, this);
        pluginWrapper.setPluginFactory(getPluginFactory());
        pluginWrapperMap.put(pluginId, pluginWrapper);
        resolvePlugins.add(pluginWrapper);
    }

    private void validatePluginDescriptor(PluginDescriptor pluginDescriptor) {
        log.info("插件读取参数校验...");
    }

    @Override
    public void startPlugins() {
        for (PluginWrapper pluginWrapper : resolvePlugins) {
            PluginState pluginState = pluginWrapper.getPluginState();
            if (PluginState.DISABLED != pluginState && PluginState.STARTED != pluginState) {
                log.info("Start plugin '{}'", pluginWrapper.getPluginDescriptor().getPluginId());
                // 回调Plugin.start()方法
                pluginWrapper.getPlugin().start();
                pluginWrapper.setPluginState(PluginState.STARTED);
                startedPlugins.add(pluginWrapper);
                // 发布状态改变事件
                firePluginStateEvent(new PluginStateEvent(this, pluginState, pluginWrapper));
            }
        }
    }

    private void firePluginStateEvent(PluginStateEvent event) {
        for (PluginStateListener pluginStateListener : pluginStateListeners) {
            pluginStateListener.pluginStateChanged(event);
        }
    }

    public void addPluginStateListener(PluginStateListener pluginStateListener) {
        pluginStateListeners.add(pluginStateListener);
    }

    @Override
    public void stopPlugins() {

    }

    @Override
    public String loadPlugins(Path pluginPath) {
        return null;
    }

    @Override
    public Path getPluginRoot() {
        return pluginRoot;
    }

    public PluginRepository getPluginRepository() {
        return pluginRepository;
    }

    public PluginLoader getPluginClassLoader() {
        return pluginClassLoader;
    }

    public PluginDescriptorFinder getPluginDescriptorFinder() {
        return pluginDescriptorFinder;
    }

    public PluginFactory getPluginFactory() {
        return pluginFactory;
    }

    @Override
    public List<PluginWrapper> getResolvePlugins() {
        return resolvePlugins;
    }

    @Override
    public List<PluginWrapper> getStartedPlugins() {
        return startedPlugins;
    }
}